/*
    This is a set of libraries to assist in OpenGl and Other Applications
    Copyright (C) 2008  The Modular Engine Team

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
#define SERVERMODULE
#include "ServerModule.h"

namespace ModularEngine
{

	void listenForClients( void *dummy )
	{
		while( ServerModule::getSingleton().mbRunning )
		{

			Client *nClient = new Client();
			nClient->mClient = INVALID_SOCKET;
			sockaddr cSock;
			int len = sizeof( cSock );
			// Wait for client to connect
			nClient->mClient = accept( ServerModule::getSingleton().ListenSocket, &cSock, &len );
			if( nClient->mClient == INVALID_SOCKET )
			{
				std::cout << "Server: accept client failed." << std::endl;
			} else
			{
				nClient->mnClientNumber = ServerModule::getSingleton().mnCurrClient++;
				// Wait for mutex releasal and use
				if( WaitForSingleObject( ServerModule::getSingleton().mClientAdd, INFINITE ) == WAIT_OBJECT_0 )
				{
					bind( nClient->mClient, &cSock, len );
					// Add a new client to the list
					ServerModule::getSingleton().mvNewClients.push_back( nClient );

					// release the mutext
					ReleaseMutex( ServerModule::getSingleton().mClientAdd );
				}
			}
		}
	}

	void recieveMessages( void *pClient )
	{

		Client *nClient = (Client *)pClient;		
		char buffer[PACKET_BUFFER_LENGTH];
		int iSize = 0;
		NetworkMessage nMsg;
		while( (iSize = recv( nClient->mClient, (char *)&nMsg, sizeof( NetworkMessage ) - sizeof(int)*2, 0 )) > 0 )
		{
			nMsg.nSize = iSize - sizeof( int ) * 2;
			nMsg.nUser = nClient->mnClientNumber;
			
			if( WaitForSingleObject( ServerModule::getSingleton().mMessageAdd, INFINITE ) == WAIT_OBJECT_0)
			{
				nClient->mMessages.push_back( nMsg );
				ReleaseMutex( ServerModule::getSingleton().mMessageAdd );
			} 
		}
	}

	int ServerModule::initialize()
	{
		nCurrMessageNumber = 0;
		mnCurrClient = 0;
		mbHost = false;
		mbRunning = false;
		result = 0;
		ptr = 0;
		ListenSocket = INVALID_SOCKET;
		return 1;
	}

	int ServerModule::initHost()
	{

		if( !mbRunning )
		{
			// Init the server
			console( "Initializing Server..." );
			if( (iResult = WSAStartup( MAKEWORD( 2, 2 ), &wsaData )) != 0 )
			{
				console( "Server: WSAStartup failed! : %d", WSAGetLastError() );
				return 0;
			}
				
			memset( &hints, 0, sizeof( hints ) );
			// Set address format to IP/Port # style
			hints.ai_family = AF_INET;
			// Set the socket type to stream
			// streams are more reliable
			// and transfer in a specific order.
			hints.ai_socktype = SOCK_STREAM;
			// The Protocal is TCP (virtual circuits)
			// this goes along with streams
			hints.ai_protocol = IPPROTO_TCP;
			hints.ai_flags = AI_PASSIVE;

			
			if( (iResult = getaddrinfo( 0, DEFAULT_PORT, &hints, &result )) != 0 )
			{
				console( "Server: Getaddrinfo failed. : %d", WSAGetLastError() );
				WSACleanup();
				return 0;
			}

			// Create the server's listening socket
			ListenSocket = socket( result->ai_family, result->ai_socktype, result->ai_protocol );
			if( ListenSocket == INVALID_SOCKET )
			{
				console( "Server: Error at socket() : %d", WSAGetLastError() );
				freeaddrinfo( result );
				WSACleanup();
				return 0;
			}
	
			// Bind the socket to this ipaddress/port
			if( (iResult = bind( ListenSocket, result->ai_addr, (int)result->ai_addrlen ) ) == SOCKET_ERROR )
			{
				
				console( "Server: bind failed. : %d", WSAGetLastError() );
				freeaddrinfo( result );
				closesocket( ListenSocket );
				WSACleanup();
				return 0;
			}

			// Set the max number of waiting clients 
			if( listen( ListenSocket, SOMAXCONN ) == SOCKET_ERROR )
			{
				console( "Server: Could not listen : %d", WSAGetLastError() );
				closesocket( ListenSocket );
				WSACleanup();
				return 0;
			}

			// Server is successful and is now running
			mbRunning = true;
			mbHost = true;
			// Prevent dual access to some things.
			mClientAdd = CreateMutex( 0, 0, "CLIENTADDMUTEX" );
			mMessageAdd = CreateMutex( 0, 0, "MESSAGEADDMUTEX" );

			// Start the client acceptance thread.
			_beginthread( listenForClients, 0, 0 );

			//console( result->ai_addr->sa_data );

			console( "Server: Listening For Clients..." );

		}

		return 1;
	}

	void ServerModule::endHost()
	{
		if( mbRunning )
		{
			mbRunning = false;			
			closesocket( ListenSocket );
			WSACleanup();
		}

	}

	int ServerModule::run()
	{
		if( mbHost && mbRunning )
		{
				// Check for New Clients Loop
				if( WaitForSingleObject( ServerModule::getSingleton().mClientAdd, INFINITE ) == WAIT_OBJECT_0 )
				{
					// Move new clients to client list
					for( std::vector<Client *>::iterator i = mvNewClients.begin(); i != mvNewClients.end(); i++ )
					{
						mvClients.push_back( (*i) );
						console( "Sending Message" );
						char buf[] = "I'm the server";
						send( (*i)->mClient, (const char *)&buf, strlen(buf)+1, 0 );
						Client *cli = (*mvClients.rbegin());
						_beginthread( recieveMessages, 0, (void *)cli);
						console( "Server: New Client Accepted" );
					}
					
					mvNewClients.clear();

					ReleaseMutex( ServerModule::getSingleton().mClientAdd );
				}

				
				// New Messages Loop
				if( WaitForSingleObject( mMessageAdd, INFINITE ) == WAIT_OBJECT_0 )
				{
					for( std::vector<Client *>::iterator i = mvClients.begin(); i != mvClients.end(); i++ )
					{

						for( std::list<NetworkMessage>::iterator j = (*i)->mMessages.begin(); j != (*i)->mMessages.end(); j++ )
						{
							if( processMessage )
								(*processMessage)( *j );
							else
								console( "Error: No Server Message Processor Given" );
						}

						// Messages should be processed.  Clear
						(*i)->mMessages.clear();
					}
					
					ReleaseMutex(mMessageAdd);
				}

		}

		return 1;
	}
	
	void ServerModule::sendMessage( NetworkMessage nMsg, int nWho, int nUser )
	{
		nMsg.nMsgNumber = nCurrMessageNumber++;
		//cout << "mNum: " << nMsg.nMsgNumber << " Type: " << nMsg.nMsgType << " Size: " << nMsg.nSize << " User: " << nMsg.nUser << endl;
		if( nMsg.nSize < 0 )
			nMsg.nSize = 0;
		if( nMsg.nSize > PACKET_BUFFER_LENGTH )
			nMsg.nSize = PACKET_BUFFER_LENGTH;
			
		if( nWho == SM_ALL )
		{
			// Send Message to all Users
			for( int i = 0; i < mvClients.size(); i++ )
			{
				send( mvClients[i]->mClient, (char *)&nMsg, nMsg.nSize+sizeof(int)*2, 0 );
			}		
			
		} else if( nWho == SM_ONE )
		{
			// Send Message To One User
			send( mvClients[nUser]->mClient, (char *)&nMsg, nMsg.nSize+sizeof(int)*2, 0 );

		} else 
		{
			console( "Error: sendMessage(...) : nWho is invalid" );
		}
		/* Insert send to user or all users stuff here */
			
		Sleep(1); // I hate this		
	}
	
	void ServerModule::sendMessage( int nMessage, int nWho, int nUser, char *pBuffer, int nSize)
	{
		if( nSize > PACKET_BUFFER_LENGTH )
		{
			sendMessage( GM_MSG_BEGIN, nWho, nUser );
			int sizeRemaining = nSize;
			
			float packets = (float)nSize/PACKET_BUFFER_LENGTH;
			if( packets != (float)((int)packets) )
				packets = (int)packets+1;

			for( int i = 0; i < packets; i++ )
			{
				NetworkMessage nMsg;
				nMsg.nMsgType = nMessage;
				if( sizeRemaining > PACKET_BUFFER_LENGTH )
					nMsg.nSize = PACKET_BUFFER_LENGTH;
				else
					nMsg.nSize = sizeRemaining;
			
				
				memcpy( &nMsg.cBuffer, pBuffer + (PACKET_BUFFER_LENGTH)*i, nMsg.nSize );	
				//cout << "Buffer: " << nMsg.cBuffer << endl;
				sendMessage( nMsg, nWho, nUser );
				sizeRemaining -= PACKET_BUFFER_LENGTH;			
				
			}
			sendMessage( GM_MSG_END, nWho, nUser );		

		} else {
			NetworkMessage nMsg;
			memcpy( &nMsg.cBuffer, pBuffer, nSize );

			nMsg.nSize = nSize;
			nMsg.nMsgType = nMessage;
			sendMessage( nMsg, nWho, nUser );
		}		
	}

}
